/**
 * Neal Mi
 * email: imzw.net@gmail.com
 * 
 * Any problem, please contact me via email. Please use following format as Subject.
 * format: OrgChart  -  your problem - your name
 * eg: OrgChart - node in a bad position -  neal
 * 
 * 有任何问题，请通过电子邮件联系我。使用以下的格式做为邮件的主题。
 * 格式： OrgChart - 问题的一句话描述 - 你的名字
 * 示例： OrgChart - 节点错位 - Peter Wang 
 **/
package mobi.imzw.control{
	
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ICollectionView;
	import mx.collections.IViewCursor;
	import mx.containers.Canvas;
	import mx.controls.HRule;
	import mx.controls.VRule;
	import mx.controls.treeClasses.DefaultDataDescriptor;
	import mx.controls.treeClasses.ITreeDataDescriptor;
	import mx.core.UIComponent;
	
	[Event(name="nodeClick", type="mobi.imzw.control.OrgChartEvent")]
	[Event(name="nodeMouseOver", type="mobi.imzw.control.OrgChartEvent")]
	[Event(name="nodeMouseOut", type="mobi.imzw.control.OrgChartEvent")]

	public class OrgChart extends Canvas{
		
		private var _data:ICollectionView;
		private var dataProviderChanged:Boolean=false;
		private var _treeDataDesciptor:ITreeDataDescriptor = new DefaultDataDescriptor;
		
		private var _depth:int=0;
		private var _currentDepth:int=0;
		
		/*
		 They're should be better.
		*/
		private var _hItemWidth:Number=80;			//横向的节点
		private var _hItemHeight:Number=40;
		
		private var _vItemWidth:Number=25;			//纵向的节点
		private var _vItemHeight:Number=80; 
		
		private var _verticalSpacing:Number=40;		//间隔
		private var _horizonalSpacing:Number=10;	//
		
		
		
		private var _nodes:ArrayCollection = new ArrayCollection();
		/* Unused */
		private var _edges:ArrayCollection = new ArrayCollection();
		
		private var _currentExpandedNode:IOrgChartNode;
		
		public function OrgChart(){
			super();
		}
		
		public function get depth():int{
			return _depth;
		}
		private function calculateDepth(data:ICollectionView):void{
			for(var cursor:IViewCursor = data.createCursor(); !cursor.afterLast; cursor.moveNext()){
				if(_treeDataDesciptor.isBranch(cursor.current, data) 
					&&  _treeDataDesciptor.getChildren(cursor.current, data).length != 0){
					
					_currentDepth++;
					
					if(_currentDepth > _depth){
						_depth = _currentDepth;
					}
					
					var __tmp:ICollectionView = _treeDataDesciptor.getChildren(cursor.current, data);
					calculateDepth(__tmp);
					
					_currentDepth--;
				}
			}	
		}
	
		/**
		 * create nodes,
		 * */
		private function _createSubNodes(data:ICollectionView, parentNode:IOrgChartNode):void{
			for(var cursor:IViewCursor = data.createCursor(); !cursor.afterLast; cursor.moveNext()){
				
				var node:IOrgChartNode = _createNode(cursor.current, parentNode);
				
				if(_treeDataDesciptor.isBranch(cursor.current, data) 
					&&  _treeDataDesciptor.getChildren(cursor.current, data).length != 0){
					
					//I know this is not good, but...
					if(cursor.current.@type != "more"){
						var __tmp:ICollectionView = _treeDataDesciptor.getChildren(cursor.current, data);
						_createSubNodes(__tmp, node);
					}
				}
			}	
		}
		private var _maxX:Number=0;
		/**
		 * Wrap data with IOrgChartNode
		 * */
		private function _createNode(data:Object, parentNode:IOrgChartNode):IOrgChartNode{
			
			var node:IOrgChartNode = new DefaultOrgChartNode();
			
			node.addEventListener(MouseEvent.CLICK, nodeClick);
			node.addEventListener(MouseEvent.MOUSE_OVER, nodeMouseOver);
			node.addEventListener(MouseEvent.ROLL_OVER, nodeRollOver);
			node.addEventListener(MouseEvent.MOUSE_OUT, nodeMouseOut);
			
			node.parentNode = parentNode;
			node.data = data;
			
			//前两级是横的，其他为竖的
			if(_depth<2){
				if(node.depth < 1){
					node.width = hItemWidth;
					node.height = hItemHeight;
					node.oriented = "h";
				}else{
					node.width = vItemWidth;
					node.height = vItemHeight;
				}							
			}else{
				if(node.depth < 2){
					node.width = hItemWidth;
					node.height = hItemHeight;
					node.oriented = "h";
				}else{
					node.width = vItemWidth;
					node.height = vItemHeight;
				}				
			}
			
			
			//起始时，根节点在最左边
			if(parentNode == null){
				node.x = _horizonalSpacing;
				node.y = _verticalSpacing;
				//_maxX = node.width + _horizonalSpacing;
			}else{
				if(node.previousSibling == null){
					//与父节点在同一中轴线上
					trace(node);
					node.x = parentNode.x + (parentNode.width - node.width)/2;					
					//我们只保存最大值
					_maxX = Math.max(node.x + node.width, parentNode.x + parentNode.width);
				}else{
					
					if(node.depth > 1 ){
						//在MouseOver显示子节点的情况下，只需要正常的根据 _horizonalSpacing 排列即可
						node.x = node.previousSibling.x + node.previousSibling.width  + _horizonalSpacing;
						if(node.depth == 2){
							_maxX = node.x + node.width;
						}						
					}else{
						node.x = _maxX + _horizonalSpacing;	
						_maxX = node.x + node.width;
					}
				}
				//移动父节点
				updateParentNodePosition(node.parentNode );				
				node.y = parentNode.y + parentNode.height + _verticalSpacing;
			}
			
			_nodes.addItem(node);
			
			return node;
		}
		/**
		 * 递归移动所有父节点的位置。
		 * Recursive update the positon of parent node. 
		 * */
		private function updateParentNodePosition(node:IOrgChartNode):void{
			if(node != null){
				var subs:ArrayCollection = node.subNodes;
				var lastChild:IOrgChartNode = subs.getItemAt(subs.length - 1 ) as IOrgChartNode;
				var firstChild:IOrgChartNode = subs.getItemAt(0) as IOrgChartNode;
				if(node.depth > 1){
					//重排子节点
					updateChildrenPosition(node);
				}else{
					//移动父节点到所有子节点的中心位置
					node.x = firstChild.x + ( lastChild.x - firstChild.x + lastChild.width - node.width) / 2;
					
				}
				//递归更新直到根节点
				updateParentNodePosition(node.parentNode); 
			}
		}
		/**
		 * 在MouseOver时显示子节点的情况下，我们要重新排列所有子节点的位置。
		 * In this case: Only show children when mouse over an Node. 
		 * Here reposition the children.
		 * */
		private function updateChildrenPosition(node:IOrgChartNode):void{
			var subs:ArrayCollection = node.subNodes;
			if(subs.length == 1) return;
			var lastChild:IOrgChartNode = subs.getItemAt(subs.length - 1 ) as IOrgChartNode;
			var firstChild:IOrgChartNode = subs.getItemAt(0) as IOrgChartNode;
			var w:Number = (lastChild.x + lastChild.width - firstChild.x);
			
			//计算子节点的起始点
			var start:Number = Math.max(node.x + node.width/2 - ( w - (node.width  + _horizonalSpacing)/2 ), node.parentNode.firstChild.x ); 
			
			//重新设置位置
			for(var cursor:IViewCursor=subs.createCursor(); !cursor.afterLast; cursor.moveNext()){
				var child:IOrgChartNode = cursor.current as IOrgChartNode;
				if(child.previousSibling == null){
					child.x = start;
				}else{
					child.x = child.previousSibling.x + child.previousSibling.width + _horizonalSpacing;
				}
			}
			//更新当前最大的 x 值
			if(lastChild.x > _maxX){
				_maxX = lastChild.x + lastChild.width;
			}
		}
		
		
		override protected function createChildren():void{
			super.createChildren();	
		}
		private function addToContainer(child:DisplayObject):void{
			addChild(child);
		}
		
		override protected function measure():void{
			super.measure();
		}
		
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			//这里添加所有节点到OrgChart上，并画线
			// Render the all node, create lines.
			if(dataProviderChanged){
				for(var cursor:IViewCursor = _nodes.createCursor(); !cursor.afterLast; cursor.moveNext()){
					var node:IOrgChartNode = cursor.current as IOrgChartNode;
					
					createLines(node);
					
					// 第三级以下节点是鼠标移上去才显示相应的子节点，
					// 但是初始时要默认展开第一个市（或部门）下面的子节点。
					
					if(node.depth > 1){
						node.collapse();
						//确保所有子节点都已经添加到OrgChart
						//ensure all children has been rendered.
						if(node.nextSibling == null){
							expandOne(node.parentNode);
						}
					}	
									
					addToContainer(node as UIComponent);
				
				}
					
				dataProviderChanged = false;
			}
		}
		private function expandOne(node:IOrgChartNode):void{
			var subs:ArrayCollection = node.subNodes;
			for(var i:int=0; i<subs.length; i++){
				if(subs.getItemAt(i).hasChildren && subs.getItemAt(i).data.@type != "more"){
					subs.getItemAt(i).expand();
					break;
				}
			}
		}
		
		private function createLines(node:IOrgChartNode):void{
			//画当前节点下面的短竖线
			if(node.hasChildren){
				var headVLine:VRule = new VRule();
				headVLine.x = node.x + node.width/2;
				headVLine.y = node.y + node.height;
				headVLine.height = _verticalSpacing/2;
				headVLine.width = 2;
				headVLine.setStyle("strokeWidth",4);
				headVLine.setStyle("strokeColor",0x999999);
				node.addLine(headVLine);
				addToContainer(headVLine);	
			}
			
			var subs:ArrayCollection = node.subNodes;
			
			//画子节点上面的横线，跨度从第一个子节点的中心到最后一个子节点的中心
			if (subs.length > 1){
				var hLine:UIComponent = new HRule();
				
				var firstChild:IOrgChartNode = subs.getItemAt(0) as IOrgChartNode;
				var lastChild:IOrgChartNode  = subs.getItemAt(subs.length -1) as IOrgChartNode;
				
				hLine.x =  firstChild.x + firstChild.width/2, 
				hLine.y = node.y + node.height + _verticalSpacing/2;
				hLine.setStyle("strokeWidth",2);
				hLine.setStyle("strokeColor",0x999999);
				hLine.height = 2;
				hLine.width = lastChild.x+lastChild.width/2 - (firstChild.x+firstChild.width/2);			
				addToContainer(hLine);
				node.addLine(hLine);
			}
			//画每个子节点头上的短竖线
			for(var i:int=0; i< subs.length; i++){
				var child:IOrgChartNode = subs.getItemAt(i) as IOrgChartNode;
				
				var vline:VRule = new VRule();
				vline.x = child.x + child.width/2;
				vline.y = child.y - _verticalSpacing/2;
				
				vline.height = _verticalSpacing/2;
				vline.width = 1;
				vline.setStyle("strokeWidth",2);
				vline.setStyle("strokeColor",0x999999);
				node.addLine(vline);
				addToContainer(vline);	
			}
		}
		//更改数据源后操作
		override protected function commitProperties():void{
			super.commitProperties();
			
			if(dataProviderChanged){
				
				//remove all children ，
				_removeAllChildren();
				
				_nodes = new ArrayCollection();
				_edges = new ArrayCollection();
				
				//reset the depth, should recalculate
				//重新计算树深度值
				_depth = 0;
				calculateDepth(_data);
				_maxX = 0;
				
				_createSubNodes(_data, null);
				invalidateDisplayList();
			}
		}
		
		private function _removeAllChildren():void{
			removeAllChildren();
		}
		
		////////////////////////////////////////////////////////////
		// Getters and Setters
		////////////////////////////////////////////////////////////
		public function set dataProvider(data:ICollectionView):void{
			_data = data;
			dataProviderChanged = true;
			invalidateProperties();
		}
		
		public function get hItemWidth():Number{
			return _hItemWidth;
		}
		
		public function set hItemWidth(w:Number):void{
			_hItemWidth = w;
		}
		
		public function get hItemHeight():Number{
			return _hItemHeight;
		}
		
		public function set hItemHeight(h:Number):void{
			hItemHeight = h;
		}
		
		
		public function get vItemWidth():Number{
			return _vItemWidth;
		}
		
		public function set vItemWidth(w:Number):void{
			_vItemWidth = w;
		}
		
		public function get vItemHeight():Number{
			return _vItemHeight;
		}
		
		public function set vItemHeight(h:Number):void{
			_vItemHeight = h;
		}
		
		private function nodeRollOver(e:MouseEvent):void{
			var n:IOrgChartNode = e.currentTarget as IOrgChartNode;
			if(n.depth > 1 && n.hasChildren){
				var subs:ArrayCollection = n.parentNode.subNodes;
				for(var i:int = 0; i< subs.length; i++){
					if(n == subs.getItemAt(i) && n.data.@type != "more"){
						n.expand();
					}else{
						subs.getItemAt(i).collapse();
					}
				}
			}
		}
		private function nodeMouseOver(e:MouseEvent):void{
			if(willTrigger(OrgChartEvent.NODE_MOUSE_OVER)){
				dispatchEvent(new OrgChartEvent(OrgChartEvent.NODE_MOUSE_OVER, e.currentTarget as IOrgChartNode));
			}
		}
		private function nodeMouseOut(e:MouseEvent):void{
			if(willTrigger(OrgChartEvent.NODE_MOUSE_OUT)){
				dispatchEvent(new OrgChartEvent(OrgChartEvent.NODE_MOUSE_OUT, e.currentTarget as IOrgChartNode));
			}
		}
		
		private function nodeClick(e:MouseEvent):void{
			if(willTrigger(OrgChartEvent.NODE_CLICK)){
				dispatchEvent(new OrgChartEvent(OrgChartEvent.NODE_CLICK, e.currentTarget as IOrgChartNode));
			}
		}
		
	}
}